package main;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class ReflectionMethods {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
		String classPath = "com.model.Student";
		
		Class<?> reflectionClass = null;
		
		try {
			reflectionClass = Class.forName(classPath);
		} catch (ClassNotFoundException e) {
			System.out.println("Class not found!!!");
			e.printStackTrace();
		}
		
		//1. GET ALL PUBLIC METHODS
		
//		Method[] methods = reflectionClass.getMethods();
//		
//		for(Method method :  methods){
//			
//			System.out.println("Method: " + method.getName());
//
//			// get parameters and return type of method
//			Class<?>[] parametersMethod = method.getParameterTypes();
//			for(Class<?> param :  parametersMethod){
//				System.out.println("\t" + "Param: " + param.getName());
//			}
//			
//			//get return type
//			System.out.println("\tReturn type: " + method.getReturnType().getName());
//		}
		
		
		// 2. INVOKING A METHOD
		Method method = null;
		
		try {
			method = reflectionClass.getDeclaredMethod("toString", null);
		} catch (NoSuchMethodException | SecurityException e) {
			System.out.println("Method can't find!!!");
			e.printStackTrace();
		}
		
		try {
			Constructor<?> constructor;
			
			constructor = reflectionClass.getDeclaredConstructor(String.class, String.class, int.class, double.class, double.class, double.class);
			
			constructor.setAccessible(true);
			
			Object student = constructor.newInstance("SV2", "Victor Vu", 20, 10, 7, 5);
			
			String info = (String) method.invoke(student, null);
			
			System.out.println("Student: " + info);
		} 
		catch (NoSuchMethodException | SecurityException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException | IllegalArgumentException
				| InvocationTargetException e) {
			System.out.println("Method can't call because illegal access!!!");
			e.printStackTrace();
		}
		
		// 3. SHOW ALL GETTER AND SETTER METHODS 
		//printGetterSetterMethods(reflectionClass);
		
		
		//4. PRIVATE METHOD
		
		Method privateMethod = null;
		
		try {
			privateMethod = reflectionClass.getDeclaredMethod("calculateAverage", double.class, double.class, double.class);
			privateMethod.setAccessible(true); //solution
		} catch (NoSuchMethodException | SecurityException e) {
			System.out.println("Method can't find!!!");
			e.printStackTrace();
		}
		
		try {
			Constructor<?> constructor;
			
			constructor = reflectionClass.getDeclaredConstructor(String.class, String.class, int.class);
			
			constructor.setAccessible(true); 
			
			Object student = constructor.newInstance("SV2", "Victor Vu", 20);
			
			double average = (double) privateMethod.invoke(student, 8, 9, 10);
			
			System.out.println("The average: " + average);
		} 
		catch (NoSuchMethodException | SecurityException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException | IllegalArgumentException
				| InvocationTargetException e) {
			System.out.println("Method can't call because illegal access!!!");
			e.printStackTrace();
		}
		
	}

	private static void printGetterSetterMethods(Class<?> aClass) {
		
		Method[] methods = aClass.getMethods();
		
		for(Method method : methods){
			if(isGetter(method)){
				System.out.println("Getter: " + method);
			}
			if(isSetter(method)){
				System.out.println("Setter: " + method);
			}
		}
		
	}

	private static boolean isGetter(Method method) {
		
		if(!method.getName().startsWith("get"))	return false;
		if(method.getParameterTypes().length != 0)	return false;
		if(void.class.equals(method.getReturnType()))	return false;
		
		return true;
	}

	private static boolean isSetter(Method method) {
		
		if(!method.getName().startsWith("set"))	return false;
		if(method.getParameterTypes().length != 1)	return false;
		
		return true;
	}
	
}













